home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 4 / Apprentice-Release4.iso / Source Code / C / Applications / MacPerl 5.0.3 / MacPerl Source ƒ / MacPerl5 / MPConsole.cp < prev    next >
Encoding:
Text File  |  1995-11-28  |  12.4 KB  |  603 lines  |  [TEXT/MPS ]

  1. /*********************************************************************
  2. Project    :    MacPerl            -    Standalone Perl
  3. File        :    MPConsole.cp    -    Console interface for GUSI
  4. Author    :    Matthias Neeracher
  5. Language    :    MPW C/C++
  6.  
  7. $Log: MPConsole.cp,v $
  8. Revision 1.2  1994/05/04  02:49:57  neeri
  9. Safer Interrupts.
  10.  
  11. Revision 1.1  1994/02/27  23:04:58  neeri
  12. Initial revision
  13.  
  14. Revision 0.2  1993/08/30  00:00:00  neeri
  15. ShowWindow -> DoShowWindow
  16.  
  17. Revision 0.1  1993/08/14  00:00:00  neeri
  18. Remember rectangles    
  19.  
  20. *********************************************************************/
  21.  
  22. #include <GUSIFile_P.h>
  23.  
  24. #include <Resources.h>
  25. #include <Windows.h>
  26. #include <Errors.h>
  27. #include <Folders.h>
  28. #include <PLStringFuncs.h>
  29. #include <LowMem.h>
  30. #include <OSEvents.h>
  31.  
  32. #include <ioctl.h>
  33. #include <sys/types.h>
  34. #include <Signal.h>
  35. #include <ctype.h>
  36.  
  37. #include "MPConsole.h"
  38.  
  39. extern "C" {
  40. #include "MPGlobals.h"
  41. #include "MPAppleEvents.h"
  42. #include "MPWindow.h"
  43. #include "MPFile.h"
  44. #include "MPMain.h"
  45. }
  46.  
  47. class MPConsoleSocket;                             // That's what this file's all about
  48.  
  49. class MPConsoleSocket : public Socket    {        
  50.     friend class MPConsoleSocketDomain;    
  51.     friend void CloseConsole(Ptr cookie);
  52.     friend Boolean DoRawConsole(Ptr cookie, char theChar);
  53.     friend void HarvestConsole(DPtr doc, MPConsoleSocket * sock);
  54.     friend int MPConsoleSpin(spin_msg msg, long arg);
  55.     
  56.                     MPConsoleSocket(DPtr window);
  57.                     
  58.     virtual         ~MPConsoleSocket();
  59.     
  60.     DPtr                    window;
  61.     Handle                input;
  62.     Boolean                nonblocking;
  63.     Boolean                eof;
  64.     Boolean                raw;
  65. public:
  66.     void             SetRaw(Boolean newRaw);
  67.     virtual int    read(void * buffer, int buflen);
  68.     virtual int write(void * buffer, int buflen);
  69.     virtual int    fcntl(unsigned int cmd, int arg);
  70.     virtual void pre_select(Boolean wantRead, Boolean wantWrite, Boolean wantExcept);
  71.     virtual int select(Boolean * canRead, Boolean * canWrite, Boolean * exception);
  72.     virtual void post_select(Boolean wantRead, Boolean wantWrite, Boolean wantExcept);
  73.     virtual int    ioctl(unsigned int request, void *argp);
  74.     virtual int    isatty();
  75. };    
  76.  
  77. class MPConsoleSocketDomain : public FileSocketDomain {
  78.     enum {
  79.         stdInput,
  80.         stdOutput,
  81.         stdError,
  82.         stdConsole,
  83.         userConsole
  84.     } consoleClass;
  85. public:
  86.     MPConsoleSocketDomain()    :    FileSocketDomain(AF_UNSPEC, true, false)    {    }
  87.     
  88.     virtual Boolean Yours(const GUSIFileRef & ref, Request request);
  89.     virtual Socket * open(const GUSIFileRef & ref, int oflag);
  90. };
  91.  
  92. MPConsoleSocketDomain    MPConsoleSockets;
  93.  
  94. #if !defined(powerc) && !defined(__powerc)
  95. #pragma segment MPConsole
  96. #endif
  97.  
  98. /************************ MPConsoleSocket members ************************/
  99.  
  100. void HarvestConsole(DPtr doc, MPConsoleSocket * sock)
  101. {                
  102.     HLock((*doc->theText)->hText);
  103.     
  104.     char * chr = *(*doc->theText)->hText + (*doc->theText)->teLength;
  105.     char * end = *(*doc->theText)->hText + doc->u.cons.fence;
  106.  
  107.     if (gGotEof == doc) {
  108.         PtrAndHand(end, sock->input, chr - end);
  109.         
  110.         doc->u.cons.fence = (*doc->theText)->teLength;
  111.         sock->eof            = true;
  112.     } else 
  113.         while (chr-- > end)
  114.             if (*chr == '\n') {
  115.                 PtrAndHand(end, sock->input, ++chr - end);
  116.                 doc->u.cons.fence = chr - *(*doc->theText)->hText;
  117.                 
  118.                 break;
  119.             }
  120.     
  121.     HUnlock((*doc->theText)->hText);
  122. }
  123.  
  124. MPConsoleSocket::MPConsoleSocket(DPtr window)
  125.     : window(window)
  126. {
  127.     nonblocking                    =    false;
  128.     eof                            =    false;
  129.     raw                            =     false;
  130.     input                            =    NewHandle(0);
  131.     
  132.     if (window)
  133.         window->u.cons.cookie    =    Ptr(this);
  134. }
  135.  
  136. void CloseConsole(Ptr cookie)
  137. {
  138.     if (cookie)
  139.         ((MPConsoleSocket *) cookie)->window = nil;
  140. }
  141.  
  142. MPConsoleSocket::~MPConsoleSocket()
  143. {
  144.     DisposeHandle(input);
  145.     
  146.     if (window) {
  147.         window->u.cons.cookie    = nil;
  148.         
  149.         if (!((WindowPeek) window->theWindow)->visible)
  150.             CloseMyWindow(window->theWindow);
  151.     }
  152. }
  153.  
  154. int MPConsoleSocket::fcntl(unsigned int cmd, int arg)
  155. {
  156.     switch (cmd)    {
  157.     case F_GETFL:
  158.         if (nonblocking)
  159.             return FNDELAY;
  160.         else
  161.             return 0;
  162.     case F_SETFL:
  163.         if (arg & FNDELAY)
  164.             nonblocking = true;
  165.         else
  166.             nonblocking = false;
  167.             
  168.         return 0;
  169.     default:
  170.         return GUSI_error(EOPNOTSUPP);
  171.     }
  172. }
  173.  
  174. int MPConsoleSocket::ioctl(unsigned int request, void *argp)
  175. {
  176.     switch (request)    {
  177.     case FIONBIO:
  178.         nonblocking    =    (Boolean) *(long *) argp;
  179.         
  180.         return 0;
  181.     case FIONREAD:
  182.         *(unsigned long *) argp    = GetHandleSize(input);
  183.         
  184.         return 0;
  185.     case FIOINTERACTIVE:
  186.         return 0;
  187.     case WIOSELECT:
  188.         if (window)
  189.             SelectWindow(window->theWindow);
  190.             
  191.         return 0;
  192.     default:
  193.         return GUSI_error(EOPNOTSUPP);
  194.     }
  195. }
  196.  
  197. int MPConsoleSocket::read(void * buffer, int buflen)
  198. {
  199.     int    avail;
  200.     
  201.     fwalk(fflush);
  202.     
  203.     avail = int(GetHandleSize(input));
  204.     
  205.     if (!avail)    {
  206.         if (eof) {
  207.             eof = false;
  208.             
  209.             return 0;
  210.         }
  211.         if (!window)
  212.             return 0;
  213.         else if (nonblocking || raw)
  214.             return GUSI_error(EWOULDBLOCK);
  215.         else {
  216.             if (!((WindowPeek) window->theWindow)->visible)
  217.                 DoShowWindow(window->theWindow);
  218.             if (!((WindowPeek) window->theWindow)->hilited)
  219.                 SelectWindow(window->theWindow);
  220.                 
  221.             window->u.cons.selected = true;
  222.             ShowWindowStatus();
  223.             
  224.             SPIN(!(avail = int(GetHandleSize(input))) && !eof && window, SP_STREAM_READ, 0);
  225.     
  226.             if (!avail && eof)
  227.                 eof = false;
  228.                 
  229.             window->u.cons.selected = false;
  230.             ShowWindowStatus();
  231.         }
  232.     }
  233.         
  234.     buflen = min(avail, buflen);
  235.     
  236.     HLock(input);
  237.     memcpy(buffer, *input, buflen);
  238.     if (avail -= buflen)
  239.         memcpy(*input, *input+buflen, avail);
  240.     HUnlock(input);
  241.     SetHandleSize(input, avail);
  242.     
  243.     return buflen;
  244. }
  245.  
  246. int MPConsoleSocket::write(void * buffer, int buflen)
  247. {
  248.     short    oldStart;
  249.     short    oldEnd;
  250.     int    len = buflen;
  251.     
  252.     if (!window)
  253.         return GUSI_error(ESHUTDOWN);
  254.  
  255.     HarvestConsole(window, this);
  256.     
  257.     if (len > window->u.cons.memory) {
  258.         buffer = (void *) (Ptr(buffer) + len - window->u.cons.memory);
  259.         len = window->u.cons.memory;
  260.     }
  261.     
  262.     (*window->theText)->teLength += buflen;
  263.     EnforceMemory(window, window->theText);
  264.     (*window->theText)->teLength -= buflen;
  265.     
  266.     oldStart    =    (*window->theText)->selStart;
  267.     oldEnd    =    (*window->theText)->selEnd;
  268.     
  269.     if (oldStart >= window->u.cons.fence)
  270.         oldStart += buflen;
  271.     if (oldEnd >= window->u.cons.fence)
  272.         oldEnd += buflen;
  273.         
  274.     TESetSelect(window->u.cons.fence, window->u.cons.fence, window->theText);
  275.     TEInsert(buffer, buflen, window->theText);
  276.  
  277.     if (!((WindowPeek) window->theWindow)->visible) {
  278.         HideControl(window->vScrollBar);
  279.         HideControl(window->hScrollBar);
  280.         
  281.         DoShowWindow(window->theWindow);
  282.         if (!((WindowPeek) window->theWindow)->hilited)
  283.             SelectWindow(window->theWindow);
  284.     }
  285.  
  286.     ShowSelect(window);
  287.     DrawPageExtras(window);
  288.     
  289.     TESetSelect(oldStart, oldEnd, window->theText);
  290.  
  291.     if (window->u.cons.fence < 32767)
  292.         window->u.cons.fence += buflen;
  293.     
  294.     return buflen;
  295. }
  296.  
  297. static Boolean StatusNeedsUpdate = false;
  298.  
  299. void MPConsoleSocket::pre_select(Boolean canRead, Boolean, Boolean)
  300. {
  301.     if (canRead && window) {
  302.         StatusNeedsUpdate = window->u.cons.selected = true;
  303.         
  304.         if (!((WindowPeek) window->theWindow)->visible)
  305.             DoShowWindow(window->theWindow);
  306.     }
  307. }
  308.  
  309. void MPConsoleSocket::post_select(Boolean canRead, Boolean, Boolean)
  310. {
  311.     if (canRead && window)
  312.         StatusNeedsUpdate = window->u.cons.selected = false;
  313. }
  314.  
  315. int MPConsoleSocket::select(Boolean * canRead, Boolean * canWrite, Boolean * exception)
  316. {
  317.     int        goodies     =     0;
  318.  
  319.     if (StatusNeedsUpdate) {
  320.         ShowWindowStatus();
  321.         
  322.         StatusNeedsUpdate = false;
  323.     }
  324.         
  325.     if (canRead)
  326.         if (*canRead = (GetHandleSize(input) > 0 || eof))
  327.             ++goodies;
  328.     
  329.     if (canWrite) {
  330.         *canWrite = true;
  331.         ++goodies;
  332.     }
  333.     
  334.     if (exception)
  335.         *exception = false;
  336.     
  337.     return goodies;
  338. }
  339.  
  340. int MPConsoleSocket::isatty()
  341. {
  342.     return 1;
  343. }
  344.  
  345. void MPConsoleSocket::SetRaw(Boolean newRaw)
  346. {
  347.     if (raw && !newRaw) {
  348.         HLock(input);
  349.         char *    checkInput        =    *input;
  350.         char *    checkedInput    =    *input;
  351.         
  352.         for (int len = GetHandleSize(input); len--; ++checkInput)
  353.             switch (*checkInput) {
  354.             case 8:
  355.                 if (checkedInput > *input)
  356.                     --checkedInput;
  357.                 break;
  358.             case '\t':
  359.             case '\n':
  360.                 *checkedInput++ = *checkInput;
  361.                 break;
  362.             case 0x7F:
  363.                 break;
  364.             default:
  365.                 if (*checkInput >= 32)
  366.                     *checkedInput++ = *checkInput;
  367.                 break;
  368.             }
  369.         write(*input, checkedInput - *input);
  370.         HUnlock(input);
  371.         SetHandleSize(input, 0);
  372.     } else if (!raw && newRaw)
  373.         HarvestConsole(window, this);
  374.         
  375.     raw = newRaw;
  376. }
  377.  
  378. Boolean DoRawConsole(Ptr cookie, char theChar)
  379. {
  380.     if (cookie) {
  381.         MPConsoleSocket *    sock    = (MPConsoleSocket *) cookie;
  382.         if (sock->raw) {
  383.             PtrAndHand(&theChar, sock->input, 1);
  384.             
  385.             return true;
  386.         }
  387.     }
  388.     return false;
  389. }
  390.  
  391. /********************* MPConsoleSocketDomain members **********************/
  392.  
  393. Boolean MPConsoleSocketDomain::Yours(const GUSIFileRef & ref, FileSocketDomain::Request request)
  394. {
  395.     if (ref.spec || (request != willOpen))
  396.         return false;
  397.     
  398.     switch (ref.name[4] | 0x20) {
  399.     case 's':
  400.         if (gRemoteControl)
  401.             return false;
  402.         if ((ref.name[5] | 0x20) != 't' || (ref.name[6] | 0x20) != 'd')
  403.             return false;
  404.         switch (ref.name[7] | 0x20) {
  405.         case 'i':
  406.             if ((ref.name[8] | 0x20) != 'n' || ref.name[9])
  407.                 return false;
  408.             consoleClass = stdInput;
  409.             
  410.             return true;
  411.         case 'o':
  412.             if ((ref.name[8] | 0x20) != 'u' || (ref.name[9] | 0x20) != 't' || ref.name[10])
  413.                 return false;
  414.             consoleClass = stdOutput;
  415.             
  416.             return true;
  417.         case 'e':
  418.             if ((ref.name[8] | 0x20) != 'r' || (ref.name[9] | 0x20) != 'r' || ref.name[10])
  419.                 return false;
  420.             consoleClass = stdError;
  421.             
  422.             return true;
  423.         default:
  424.             return false;
  425.         }
  426.     case 'c':
  427.         if (    (ref.name[5] | 0x20) != 'o' || (ref.name[6] | 0x20) != 'n'
  428.             || (ref.name[7] | 0x20) != 's' || (ref.name[8] | 0x20) != 'o'
  429.             || (ref.name[9] | 0x20) != 'l' || (ref.name[10] | 0x20) != 'e')
  430.             return false;
  431.         switch (ref.name[11]) {
  432.         case 0:
  433.             consoleClass = stdConsole;
  434.             
  435.             return true;
  436.         case ':':
  437.             consoleClass = userConsole;
  438.             
  439.             return true;
  440.         default:
  441.             return false;
  442.         }
  443.     default:
  444.         return false;
  445.     }
  446. }
  447.  
  448. Socket * MPConsoleSocketDomain::open(const GUSIFileRef & ref, int flags)
  449. {
  450.     DPtr                doc;
  451.     Socket *            sock = nil;
  452.     char                 title[256];
  453.     Boolean            nudoc = false;
  454.     
  455.     switch (consoleClass) {
  456.     case stdInput:
  457.         flags = O_RDONLY;
  458.         
  459.         break;
  460.     case stdOutput:
  461.         flags = O_WRONLY;
  462.         
  463.         break;
  464.     case stdError:
  465.         flags = O_WRONLY;
  466.         
  467.         break;
  468.     default:
  469.         break;
  470.     }
  471.  
  472.     if (consoleClass == userConsole) {
  473.         for (doc = gConsoleList; doc; doc = doc->u.cons.next)
  474.             if (doc->kind == kConsoleWindow) {
  475.                 getwtitle(doc->theWindow, title);
  476.                 
  477.                 if (equalstring(title, (char *) ref.name+12, false, true)) {
  478.                     if (doc->u.cons.cookie)
  479.                         sock = (Socket *) doc->u.cons.cookie;
  480.  
  481.                     goto found;
  482.                 }
  483.             }
  484.             
  485.         nudoc    = true;                
  486.         doc    = NewDocument(false, kConsoleWindow);
  487.         
  488.         setwtitle(doc->theWindow, (char *) ref.name+12);
  489.         
  490.         RestoreConsole(doc);
  491.     } else {
  492.         for (doc = gConsoleList; doc; doc = doc->u.cons.next)
  493.             if (doc->kind == kWorksheetWindow) {
  494.                 if (doc->u.cons.cookie)
  495.                     sock = (Socket *) doc->u.cons.cookie;
  496.  
  497.                 goto found;
  498.             }
  499.  
  500.         nudoc = true;
  501.         doc    = NewDocument(false, kWorksheetWindow);
  502.         SetWTitle(doc->theWindow, LMGetCurApName());
  503.  
  504.         RestoreConsole(doc);
  505.     }
  506.  
  507. found:    
  508.     if (!sock) {
  509.         errno = 0;
  510.         sock     = new MPConsoleSocket(doc);
  511.         
  512.         if (sock && errno) {
  513.             if (nudoc)
  514.                 CloseMyWindow(doc->theWindow);
  515.  
  516.             delete sock;
  517.             
  518.             return nil;
  519.         }
  520.     } else
  521.         ((MPConsoleSocket *)sock)->eof = false;
  522.  
  523.     if (!(flags & 1) && doc)
  524.         doc->u.cons.fence = (*doc->theText)->teLength;
  525.     else if (nudoc)
  526.         doc->u.cons.fence = 32767;
  527.  
  528.     return sock;
  529. }
  530.  
  531. /********************* A kinder, gentler, spin **********************/
  532.  
  533. extern "C" void my_exit(int status);
  534.  
  535. int MPConsoleSpin(spin_msg spin, long)
  536. {
  537.     if (!gInBackground && GUSIInterrupt() && gRunningPerl) {
  538.         FlushEvents(-1, 0);
  539.  
  540.         if (spin == SP_AUTO_SPIN)
  541.             my_exit(-128);
  542.         else
  543.             return -1;
  544.     }
  545.         
  546.     MainEvent(spin != SP_SELECT && spin != SP_STREAM_READ);
  547.     
  548.     for (DPtr doc = gConsoleList; doc; doc = doc->u.cons.next)
  549.         if (doc->dirty) {
  550.             if (doc->u.cons.cookie) 
  551.                 HarvestConsole(doc, (MPConsoleSocket *) doc->u.cons.cookie);
  552.             doc->dirty = false;
  553.         }
  554.     
  555.     return 0;
  556. }
  557.  
  558. /********************* Raw I/O **********************/
  559.  
  560. static int EmulateStty(FILE * tempFile, char * command)
  561. {
  562.     Boolean    setRaw = true;
  563.     
  564.     if (*command == '-') {
  565.         ++command;
  566.         setRaw = !setRaw;
  567.     }
  568.     
  569.     if (!strncmp(command, "raw", 3)) {
  570.         command += 3;
  571.     } else if (!strncmp(command, "sane", 3) && isspace(command[4])) {
  572.         command += 4;
  573.         setRaw = !setRaw;
  574.     } else
  575.         return -1;
  576.     
  577.     if (*command && !isspace(*command))
  578.         return -1;
  579.     
  580.     while (*command && isspace(*command))
  581.         ++command;
  582.     
  583.     if (!*command)
  584.         command     =     "Dev:Console";
  585.  
  586.     GUSIFileRef    ref(command, FileSocketDomain::willOpen);
  587.     
  588.     if (ref.Domain() != &MPConsoleSockets)
  589.         return -1;
  590.     
  591.     MPConsoleSocket * console = (MPConsoleSocket *) ref.Domain()->open(ref, O_RDWR);
  592.     
  593.     console->SetRaw(setRaw);
  594.     
  595.     return 0;
  596. }
  597.  
  598. void InitConsole()
  599. {
  600.     MPConsoleSockets.DontStrip();
  601.     AddWriteEmulationProc("stty", EmulateStty);
  602. }
  603.